using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using ProtoScratch.Common;

namespace ProtoScratch.Server
{
	class ServerConnection : ScratchProtocol
	{
		private static Random NextSessionId = new Random();
		private bool initialized;
		
		/// <summary>
		/// Initializes a new instance of the <see cref="ProtoScratch.Server.ServerConnection"/> class.
		/// </summary>
		internal ServerConnection (ScratchSession session, TcpClient Client) : base (session, Client.GetStream())
		{
			if (session.useSessionIDs)
				this.sessionId = NextSessionId.Next(int.MinValue, int.MaxValue);
			
			Run("ProtoScratch Server Instance");
		}
		
		/// <summary>
		/// Handles the new client connection
		/// </summary>
		protected override void HandleSession()
		{
			try
			{
				SendConfiguration();
				while (!aborted)
				{
					ReceiveMessage();
				}
			}
			catch (Exception){}
			finally
			{
				if (initialized)
					handler.ConnectionLost();
			}
		}
		
		/// <summary>
		/// Receive the next frame and send it to the handler.
		/// </summary>
		private void ReceiveMessage()
		{
			try
			{
				var Message = Receive();
				if (Message.getFlag(ScratchFlags.Internal))
				{
					switch (Message.PrimaryCommand)
					{
					case Internal.Config:
						if (Message.SecondaryCommand == Internal.Confirm && !initialized)
						{
							handler.InitializeConnection(Send, Abort);
							initialized = true;
						}
						break;
						
					case Internal.PingPong:
						if (Message.SecondaryCommand == Internal.Ping)
							Pong();
						break;
					}
				}
				else if (initialized)
					handler.MessageReceived(Message);
			}
			catch (ScratchException ex)
			{
				if (ex.Type == ScratchException.eType.Timeout)
					Ping();
				else
					throw;
			}
		}
		
		private void SendConfiguration()
		{
			var Message = new ScratchMessage(0, Internal.Config, 0);
			Message.setFlag(ScratchFlags.Internal, true);
			
			var Key = encryptor.RSAEncrypt(encryptor.encKey);
			var IV = encryptor.RSAEncrypt(encryptor.encIV);
			
			Message.pack((Int16)Key.Length);
			Message.pack(Key);
			
			Message.pack((Int16)IV.Length);
			Message.pack(IV);
			
			Send(Message);
		}
	}
}

